import pygame
import sys
import random

# Initialize Pygame
pygame.init()

# Initialize mixer for music
pygame.mixer.init()

# Load and play background music
pygame.mixer.music.load(r"connected.mp3")
pygame.mixer.music.set_volume(0.5)  # Set volume to 50%
pygame.mixer.music.play(-1)  # Loop the music indefinitely

# Screen dimensions
GRID_WIDTH = 10
GRID_HEIGHT = 20
GRID_SIZE = 30
HOLD_NEXT_SIZE = GRID_SIZE // 2  # Reduced size for HOLD and NEXT blocks
SCREEN_WIDTH = (GRID_WIDTH + 10) * GRID_SIZE  # Extra space for HOLD and NEXT areas
SCREEN_HEIGHT = GRID_HEIGHT * GRID_SIZE
GAME_OFFSET_X = 5 * GRID_SIZE  # Offset to center the game area

# Colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GRAY = (128, 128, 128)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
CYAN = (0, 255, 255)
MAGENTA = (255, 0, 255)
YELLOW = (255, 255, 0)

# Shapes
SHAPES = [
    [[1, 1, 1, 1]],  # I
    [[1, 1, 1], [0, 1, 0]],  # T
    [[1, 1, 0], [0, 1, 1]],  # Z
    [[0, 1, 1], [1, 1, 0]],  # S
    [[1, 1], [1, 1]],  # O
    [[1, 1, 1], [1, 0, 0]],  # L
    [[1, 1, 1], [0, 0, 1]]  # J
]

# Shape colors
SHAPE_COLORS = [
    (43, 156, 217),  # I
    (163, 87, 161),  # T
    (95, 161, 24),   # Z
    (228, 39, 37),   # S
    (254, 189, 12),  # O
    (248, 110, 0),   # L
    (6, 79, 181)     # J
]

# Game states
STATE_TITLE = 0
STATE_HELP = 1
STATE_GAME = 2
STATE_PAUSE = 3
STATE_SETTINGS = 4

# Initialize screen
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Tetris")

# Fonts
font = pygame.font.SysFont('Arial', 25)

# Game variables
game_state = STATE_TITLE
score = 0
level = 1
lines_cleared = 0  # Track the number of lines cleared
fall_speed = 500  # milliseconds
last_fall_time = pygame.time.get_ticks()

# Current shape
current_shape = random.choice(SHAPES)
current_color = SHAPE_COLORS[SHAPES.index(current_shape)]
current_x = GRID_WIDTH // 2 - len(current_shape[0]) // 2
current_y = 0

# Hold and next shapes
hold_shape = None
hold_color = None
can_hold = True
next_shape = random.choice(SHAPES)
next_color = SHAPE_COLORS[SHAPES.index(next_shape)]

# Grid
grid = [[BLACK for _ in range(GRID_WIDTH)] for _ in range(GRID_HEIGHT)]

def draw_text(text, font, color, surface, x, y):
    textobj = font.render(text, True, color)
    textrect = textobj.get_rect()
    textrect.topleft = (x, y)
    surface.blit(textobj, textrect)

def draw_grid():
    for y in range(GRID_HEIGHT):
        for x in range(GRID_WIDTH):
            pygame.draw.rect(screen, grid[y][x], (GAME_OFFSET_X + x * GRID_SIZE, y * GRID_SIZE, GRID_SIZE, GRID_SIZE), 0)
            pygame.draw.rect(screen, GRAY, (GAME_OFFSET_X + x * GRID_SIZE, y * GRID_SIZE, GRID_SIZE, GRID_SIZE), 1)

def draw_shape(shape, color, offset_x, offset_y, size=GRID_SIZE):
    for y, row in enumerate(shape):
        for x, cell in enumerate(row):
            if cell:
                pygame.draw.rect(screen, color, (offset_x + x * size, offset_y + y * size, size, size), 0)
                pygame.draw.rect(screen, GRAY, (offset_x + x * size, offset_y + y * size, size, size), 1)

def draw_hold():
    if hold_shape:
        draw_shape(hold_shape, hold_color, 10, 100, HOLD_NEXT_SIZE)
    draw_text('HOLD', font, WHITE, screen, 10, 50)

def draw_next():
    draw_shape(next_shape, next_color, SCREEN_WIDTH - 100, 100, HOLD_NEXT_SIZE)
    draw_text('NEXT', font, WHITE, screen, SCREEN_WIDTH - 100, 50)

def check_collision(shape, offset_x, offset_y):
    for y, row in enumerate(shape):
        for x, cell in enumerate(row):
            if cell:
                if x + offset_x < 0 or x + offset_x >= GRID_WIDTH or y + offset_y >= GRID_HEIGHT:
                    return True
                if grid[y + offset_y][x + offset_x] != BLACK:
                    return True
    return False

def merge_shape(shape, color, offset_x, offset_y):
    global score
    for y, row in enumerate(shape):
        for x, cell in enumerate(row):
            if cell:
                grid[y + offset_y][x + offset_x] = color
    # Add points for placing a block
    score += 4 * level

def remove_full_lines():
    global score, lines_cleared, level, fall_speed
    new_grid = [row for row in grid if any(cell == BLACK for cell in row)]
    cleared = GRID_HEIGHT - len(new_grid)
    lines_cleared += cleared
    
    # Update score based on the number of lines cleared
    if cleared == 1:
        score += 50
    elif cleared == 2:
        score += 125
    elif cleared == 3:
        score += 200
    elif cleared == 4:
        score += 400
    
    new_grid = [[BLACK for _ in range(GRID_WIDTH)] for _ in range(cleared)] + new_grid
    
    # Increase level and speed every 10 lines cleared
    if lines_cleared >= 10:
        level += 1
        lines_cleared -= 10
        fall_speed = max(100, fall_speed - 50)  # Increase speed, minimum 100ms
    
    return new_grid

def rotate_shape(shape):
    return [list(row) for row in zip(*shape[::-1])]

def wall_kick(shape, offset_x, offset_y):
    # Try moving the shape left, right, up to see if it fits
    for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
        if not check_collision(shape, offset_x + dx, offset_y + dy):
            return offset_x + dx, offset_y + dy
    return offset_x, offset_y

def is_t_spin(shape, offset_x, offset_y):
    # Check if the shape is T and if it is in a T-spin position
    if shape != SHAPES[1]:  # T shape
        return False
    corners = [(0, 0), (0, 2), (2, 0), (2, 2)]
    filled_corners = sum(1 for dx, dy in corners if grid[offset_y + dy][offset_x + dx] != BLACK)
    return filled_corners >= 3

def game_over():
    global game_state
    game_state = STATE_TITLE

def hard_drop():
    global current_y, grid
    while not check_collision(current_shape, current_x, current_y + 1):
        current_y += 1
    merge_shape(current_shape, current_color, current_x, current_y)
    grid = remove_full_lines()
    new_shape()

def hold_piece():
    global current_shape, current_color, hold_shape, hold_color, can_hold
    if not can_hold:
        return
    if hold_shape is None:
        hold_shape = current_shape
        hold_color = current_color
        new_shape()
    else:
        current_shape, current_color, hold_shape, hold_color = hold_shape, hold_color, current_shape, current_color
        current_x = GRID_WIDTH // 2 - len(current_shape[0]) // 2
        current_y = 0
    can_hold = False

def new_shape():
    global current_shape, current_color, current_x, current_y, next_shape, next_color, can_hold
    current_shape = next_shape
    current_color = SHAPE_COLORS[SHAPES.index(current_shape)]
    next_shape = random.choice(SHAPES)
    next_color = SHAPE_COLORS[SHAPES.index(next_shape)]
    current_x = GRID_WIDTH // 2 - len(current_shape[0]) // 2
    current_y = 0
    can_hold = True
    if check_collision(current_shape, current_x, current_y):
        game_over()

def game_loop():
    global current_shape, current_color, current_x, current_y, last_fall_time, grid, score, level, fall_speed

    screen.fill(BLACK)
    draw_grid()
    draw_shape(current_shape, current_color, GAME_OFFSET_X + current_x * GRID_SIZE, current_y * GRID_SIZE)
    draw_hold()
    draw_next()
    draw_text(f'Score: {score}', font, WHITE, screen, 10, SCREEN_HEIGHT // 2 - 50)
    draw_text(f'Level: {level}', font, WHITE, screen, 10, SCREEN_HEIGHT // 2)
    draw_text(f'Lines: {lines_cleared}', font, WHITE, screen, 10, SCREEN_HEIGHT // 2 + 50)  # Add this line

    current_time = pygame.time.get_ticks()
    if current_time - last_fall_time > fall_speed:
        if not check_collision(current_shape, current_x, current_y + 1):
            current_y += 1
        else:
            if is_t_spin(current_shape, current_x, current_y):
                score += 400  # T-spin bonus
            merge_shape(current_shape, current_color, current_x, current_y)
            grid = remove_full_lines()
            new_shape()
        last_fall_time = current_time

    pygame.display.update()

def title_screen():
    screen.fill(BLACK)
    draw_text('Tetris', font, WHITE, screen, SCREEN_WIDTH // 2 - 50, SCREEN_HEIGHT // 2 - 100)
    draw_text('Press ENTER to start', font, WHITE, screen, SCREEN_WIDTH // 2 - 100, SCREEN_HEIGHT // 2)
    draw_text('Press H for help', font, WHITE, screen, SCREEN_WIDTH // 2 - 100, SCREEN_HEIGHT // 2 + 50)
    pygame.display.update()

def help_screen():
    screen.fill(BLACK)
    draw_text('Help', font, WHITE, screen, SCREEN_WIDTH // 2 - 50, SCREEN_HEIGHT // 2 - 100)
    draw_text('Use arrow keys to move', font, WHITE, screen, SCREEN_WIDTH // 2 - 100, SCREEN_HEIGHT // 2)
    draw_text('Press Z to rotate left', font, WHITE, screen, SCREEN_WIDTH // 2 - 100, SCREEN_HEIGHT // 2 + 50)
    draw_text('Press X to rotate right', font, WHITE, screen, SCREEN_WIDTH // 2 - 100, SCREEN_HEIGHT // 2 + 100)
    draw_text('Press C to hold', font, WHITE, screen, SCREEN_WIDTH // 2 - 100, SCREEN_HEIGHT // 2 + 150)
    draw_text('Press ESC to return', font, WHITE, screen, SCREEN_WIDTH // 2 - 100, SCREEN_HEIGHT // 2 + 200)
    pygame.display.update()

def pause_screen():
    screen.fill(BLACK)
    draw_text('Paused', font, WHITE, screen, SCREEN_WIDTH // 2 - 50, SCREEN_HEIGHT // 2 - 100)
    draw_text('Press ESC to resume', font, WHITE, screen, SCREEN_WIDTH // 2 - 100, SCREEN_HEIGHT // 2)
    draw_text('Press R to restart', font, WHITE, screen, SCREEN_WIDTH // 2 - 100, SCREEN_HEIGHT // 2 + 50)
    draw_text('Press Q to quit', font, WHITE, screen, SCREEN_WIDTH // 2 - 100, SCREEN_HEIGHT // 2 + 100)
    pygame.display.update()

def settings_screen():
    screen.fill(BLACK)
    draw_text('Settings', font, WHITE, screen, SCREEN_WIDTH // 2 - 50, SCREEN_HEIGHT // 2 - 100)
    draw_text('Press UP/DOWN to change speed', font, WHITE, screen, SCREEN_WIDTH // 2 - 150, SCREEN_HEIGHT // 2)
    draw_text('Press ESC to return', font, WHITE, screen, SCREEN_WIDTH // 2 - 100, SCREEN_HEIGHT // 2 + 50)
    pygame.display.update()

# Main loop
running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.KEYDOWN:
            if game_state == STATE_TITLE:
                if event.key == pygame.K_RETURN:
                    game_state = STATE_GAME
                elif event.key == pygame.K_h:
                    game_state = STATE_HELP
            elif game_state == STATE_HELP:
                if event.key == pygame.K_ESCAPE:
                    game_state = STATE_TITLE
            elif game_state == STATE_GAME:
                if event.key == pygame.K_ESCAPE:
                    game_state = STATE_PAUSE
                elif event.key == pygame.K_LEFT:
                    if not check_collision(current_shape, current_x - 1, current_y):
                        current_x -= 1
                elif event.key == pygame.K_RIGHT:
                    if not check_collision(current_shape, current_x + 1, current_y):
                        current_x += 1
                elif event.key == pygame.K_DOWN:
                    if not check_collision(current_shape, current_x, current_y + 1):
                        current_y += 1
                elif event.key == pygame.K_z:
                    rotated_shape = rotate_shape(rotate_shape(rotate_shape(current_shape)))  # Rotate right
                    if check_collision(rotated_shape, current_x, current_y):
                        current_x, current_y = wall_kick(rotated_shape, current_x, current_y)
                    if not check_collision(rotated_shape, current_x, current_y):
                        current_shape = rotated_shape
                elif event.key == pygame.K_x:
                    rotated_shape = rotate_shape(current_shape)  # Rotate left
                    if check_collision(rotated_shape, current_x, current_y):
                        current_x, current_y = wall_kick(rotated_shape, current_x, current_y)
                    if not check_collision(rotated_shape, current_x, current_y):
                        current_shape = rotated_shape
                elif event.key == pygame.K_SPACE:
                    hard_drop()
                elif event.key == pygame.K_c:
                    hold_piece()
            elif game_state == STATE_PAUSE:
                if event.key == pygame.K_ESCAPE:
                    game_state = STATE_GAME
                elif event.key == pygame.K_r:
                    grid = [[BLACK for _ in range(GRID_WIDTH)] for _ in range(GRID_HEIGHT)]
                    score = 0
                    level = 1
                    fall_speed = 500
                    current_shape = random.choice(SHAPES)
                    current_color = SHAPE_COLORS[SHAPES.index(current_shape)]
                    current_x = GRID_WIDTH // 2 - len(current_shape[0]) // 2
                    current_y = 0
                    game_state = STATE_GAME
                elif event.key == pygame.K_q:
                    game_state = STATE_TITLE
            elif game_state == STATE_SETTINGS:
                if event.key == pygame.K_ESCAPE:
                    game_state = STATE_TITLE
                elif event.key == pygame.K_UP:
                    fall_speed = max(100, fall_speed - 50)
                elif event.key == pygame.K_DOWN:
                    fall_speed = min(1000, fall_speed + 50)

    if game_state == STATE_TITLE:
        title_screen()
    elif game_state == STATE_HELP:
        help_screen()
    elif game_state == STATE_GAME:
        game_loop()
    elif game_state == STATE_PAUSE:
        pause_screen()
    elif game_state == STATE_SETTINGS:
        settings_screen()

pygame.quit()
sys.exit()